package fieldmaps.mapalgebra.binaryOperations;

import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;

import org.apache.log4j.Logger;

import com.vividsolutions.jts.geom.Polygon;

import fieldmaps.database.DatabaseRasterMap;
import fieldmaps.exceptions.MapAlgebraException;
import fieldmaps.types.MapInfo;

public class RasterBinaryMapAlgebra {
	private static Logger LOGGER = Logger.getLogger(RasterBinaryMapAlgebra.class);
	
	private DatabaseRasterMap map1;
	private DatabaseRasterMap map2;
	
	public RasterBinaryMapAlgebra(String masterMap, String secondaryMap) throws IllegalArgumentException {
		if (masterMap == null || secondaryMap == null || masterMap.isEmpty() || secondaryMap.isEmpty()) {
			throw new IllegalArgumentException("Wrong map Names!");
		}
		
		map1 = DatabaseRasterMap.loadMap(masterMap);
		map2 = DatabaseRasterMap.loadMap(secondaryMap);
	}
	
	public DatabaseRasterMap mapOperation (ICallbackBinary callbackObject) throws MapAlgebraException {
		if (map1.getMapArea().doubleValue() != map2.getMapArea().doubleValue()) {
			throw new MapAlgebraException("Invalid maps! Both areas must be equal!");
		}
		
		Polygon polygon;
		MapInfo mapInfo = new MapInfo(map1.getMapInfo());
		DatabaseRasterMap newMap = DatabaseRasterMap.createMap(mapInfo, callbackObject.newMapName());
		
		map1.startCellIteration();
		try {
			while ((polygon = map1.getNextCell()) != null) {
				List<Polygon> polygonsIntersection = new LinkedList<Polygon>();
				List<Double> areasPolygonsIntersection = new LinkedList<Double>();
				map2.getPolygonsIntersecArea(polygon, polygonsIntersection, areasPolygonsIntersection);
				Object interpolatedValue = getInterpolatedValue (polygon, polygonsIntersection, areasPolygonsIntersection);
				Integer userData = (Integer) callbackObject.doOperation(interpolatedValue, polygon.getUserData());
				newMap.addCell(polygon.getCoordinates(), userData);
			}
		} catch (NoSuchElementException e) {
			LOGGER.info("New map: " + callbackObject.newMapName() + " successfully created!");
			return newMap;
		} catch (SQLException e) {
			LOGGER.error(e.getMessage());
		}
		map1.resetCellIteration();
		return null;
	}

	private Object getInterpolatedValue(Polygon polygon, List<Polygon> polygonsIntersection, List<Double> areasPolygonsIntersection) {
		Double polygonArea = polygon.getArea();
		Integer polygonValue = (Integer)polygon.getUserData(); 
		if (polygonValue == map1.getMapInfo().getNodata()) {
			return null;
		}
		Integer polygonsIntersectionSize = polygonsIntersection.size();
		if (polygonsIntersectionSize == 1) {
			return (int)(polygonArea / polygonsIntersection.get(0).getArea()) * (Integer)polygonsIntersection.get(0).getUserData(); 
		}
		Double value = 0.0;
		Polygon p;
		for (int i = 0; i < polygonsIntersectionSize; i++) {
			p = polygonsIntersection.get(i);
			if ((Integer)p.getUserData() == map2.getMapInfo().getNodata()) {
				return null;
			}
			value += (areasPolygonsIntersection.get(i) / p.getArea()) * (Integer)p.getUserData();		
		}
		return (new Long(Math.round(value))).intValue();
	}

}
